/**

This file is part of MaCI/GIMnet.

MaCI/GIMnet is free software: you can redistribute it and/or modify it 
under the terms of the GNU Lesser General Public License as published 
by the Free Software Foundation, either version 3 of the License, or 
(at your option) any later version.

MaCI/GIMnet is distributed in the hope that it will be useful, but WITHOUT 
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public 
License for more details.

You should have received a copy of the GNU Lesser General Public 
License along with GIMnet. (See COPYING.LESSER) If not, see 
<http://www.gnu.org/licenses/>.

**/
/**
 * \file
 * \brief MaCI - TaskCtrl Interface Client header
 * \author Antti Maula <antti.maula@tkk.fi>
 * \author Matthieu Myrsky <matthieu.myrsky@tkk.fi>
 *
 * $Id: TaskCtrlClient.hpp,v 1.10 2010-01-05 07:04:12 mmatusia Exp $
 *
 * TODO:
 * - Error message when state is error

 */
#ifndef _MACI_INTERFACE_TASKCTRLCLIENT_HPP_
#define _MACI_INTERFACE_TASKCTRLCLIENT_HPP_

#include "MaCI.hpp"
#include "thread.hpp"
#include "sync.hpp"

// These are not necessarily required here, but defined to make the
// interface easier to include.
#include "TaskCtrlData.hpp"

// Forward declare gimi::GIMI
namespace gimi {
  class GIMI;
}

namespace MaCI {
  namespace TaskCtrl {
    
    // Forward declaration.
    class CTaskCtrlClient;

    
    
    /** Callback interface class for TaskCtrl interface.
     *
     * This interface defines functions which can be used for
     * TaskCtrlClient callbacks. These functions are executed (more
     * detail per function basis) as new data arrives, and directly
     * from the context of the receiving thread. <b>Therefore, it is
     * important that the handling function is as simple and fast as
     * possible, as it will block the execution of the receiving
     * thread while executed.</b>
     */
    class CTaskCtrlClientCallback
    {
    public:

      CTaskCtrlClientCallback() {}
      virtual ~CTaskCtrlClientCallback() {}


      /** Handler function for TaskCtrlData event.
       *
       * This function is called when the TaskCtrlClient receives new
       * data from the server.
       *
       * \note <b>Keep this function simple & fast as it will block the 
       * data receiving thread while executing!</b>
       *
       * @param[in] aData       Constant reference to CTaskCtrlData element 
       *                        containing the received event.
       * @param[in] aDataSequence Sequence number of the received data. 
       *                        This should increment by one on each call.
       * @param[in] aTaskCtrlClient Reference to TaskCtrlClient 
       *                        instance which received this event.
       * @param[in] aUserPtr    User pointer.
       */
      virtual void OnTaskCtrlDataEvent(MaCI::TaskCtrl::CTaskCtrlData &aData, 
                                       const unsigned int aDataSequence,
                                       CTaskCtrlClient &aTaskCtrlClient,
                                       void * aUserPtr = NULL) = 0;
    };

    /** TaskCtrl event types.
     */
    enum ETaskCtrlEvent {
      KEventTaskCtrlData = 0 ///< TaskCtrlData element received and stored.
    };


    /** MaCI - TaskCtrl Client interface.
     * 
     * This class contains the methods and types for using
     * a MaCI::TaskCtrl service in the GIMnet.
     */
    class CTaskCtrlClient 
      : private gim::CSync,
        private gim::CThread,
        public MaCI::CMaCI
    {
    public:
      /** Constructor.
       * 
       * Constructor of the TaskCtrlClient. This requires;
       * a pointer to Initialized and connected GIMI, minor
       * number of this interface (Used as GIMI minor number)
       *
       * @param[in] aGIMIPtr Pointer to GIMI used for communication
       * @param[in] aInterfaceMinor Interface minor number. Usually
       *            used to indicate the instance of devices of same
       *            type. Can be left out if MaCISL is used.
       */     
      CTaskCtrlClient(gimi::GIMI *aGIMIPtr, 
                      const int aInterfaceMinor = -1);

      
      /** Destructor.
       */
      ~CTaskCtrlClient();

      /**
       * Adds a task to Table. 
       *
       * @param[in][out] INOUT                 Task ID of the task to add. Server will give the 
       *                                       Task ID(new or the same) as a response. It can be another if the
       *                                       server want to change it for some reason (e.g.same ID exists)                                       
       * @param[in]      aTaskSpecificXML      Task specific XML of the added task
       * @param[in]      aTimeout_ms           Timeout for waiting an event
       * @return 
       */
      bool AddTask(MaCI::TaskCtrl::TTaskCtrlTaskID &INOUT, 
                   const std::string aTaskSpecificXML,
                   unsigned int aTimeout_ms);

      /**
       * Remove task from table
       *
       * @param[in] aTaskID               Task ID of task to remove from the pool. Task ID id the taskID got from
       *                                  TTaskCtrlTaskID-structure
       * @return 
       */
      bool RemoveTask(const unsigned int TaskID);

      /**
       * Init task from table
       * 
       * @param[in] aTaskID               Task ID of the task in the table to initialize
       * @return 
       */
      bool InitTask(const unsigned int TaskID);

      /**
       * Start task from table.
       * The task have to be added to the table before calling this. The server side
       * decides if it is really started or started later 
       * (TAPIO? is it right, or does the addTask requires that it can be started)  
       *
       * @param[in] aTaskID              Task ID of the task to start
       * @return 
       */
      bool StartTask(const unsigned int TaskID);

      /**
       * Stop task from table.
       * - The task must be in the table and running when calling this to have any effect.
       *
       * @param[in] aTaskID              Task ID of the task to stop (which is added to table)
       * @return 
       */
      bool StopTask(const unsigned int TaskID);

      /**
       * Pause task from table
       * - The task must be in the table and running when calling this to have any effect. 
       * - To continue the task call ContinueTask-function.
       *
       * @param[in] aTaskID              Task ID of the task to pause (which is added to table)
       */
      bool PauseTask(const unsigned int TaskID);

      /**
       * Continue a paused task from table
       * - The task must be paused when calling this to have any effect.
       *
       * @param[in] aTaskID              Task ID of the task to continue
       */
      bool ContinueTask(const unsigned int TaskID);

  
      /**
       * Starts the Task Controller. 
       *
       * @return
       */
      bool StartTaskCtrl();

      /**
       * Stops the Task Controller. 
       * 
       *
       * @return
       */
      bool StopTaskCtrl();

      /**
       * Pauses the Task Controller
       *
       * @return 
       */
      bool PauseTaskCtrl();

      /**
       * Get the competences
       *
       * ADD TASK ID
       */
      bool GetTaskLibrary(MaCI::TaskCtrl::TTaskCtrlTaskStateArray &aStateArray,
                          std::vector<std::string> &aTasksXML,
                          unsigned int aTimeout_ms);

      /**
       * Function for using in Python
       */
      inline bool GetTaskLibrary(MaCI::TaskCtrl::TTaskCtrlTaskStateContainer &container,
                                 MaCI::TaskCtrl::TStringContainer &stringcontainer,
                                 unsigned int aTimeout_ms);

      /**
       * Add task to library
       *
       */
      bool AddTaskToLibrary(MaCI::TaskCtrl::TTaskCtrlTaskID &INOUT,
                            std::string aXml,
                            unsigned int aTimeout_ms,
                            std::string &INOUT1);
      /**
       * Remove task from library
       *
       * Tasks are identified by typeIDs of TTaskCtrlTaskID in the library
       */
      bool RemoveTaskFromLibrary(MaCI::TaskCtrl::TTaskCtrlTaskID aTaskID);
      
      /**
       * Get tasks
       */
      bool GetTaskTable(MaCI::TaskCtrl::TTaskCtrlTaskStateArray &aStateArray, 
                       std::vector<std::string> &aTasksXML,
                       unsigned int aTimeout_ms);
      /**
       * Function for using in Python
       */
      inline bool GetTaskTable(MaCI::TaskCtrl::TTaskCtrlTaskStateContainer &INOUT,
                       MaCI::TaskCtrl::TStringContainer &INOUT1,
                       unsigned int aTimeout_ms);
      /**
       * Get the state of the task
       */
      bool GetTaskState(unsigned int aTaskID,
                        MaCI::TaskCtrl::TTaskCtrlTaskState &INOUT,
                        unsigned int aTimeout_ms);


      /**
       * Waits for a task event.
       *
       * Events are sent usually when a state of a task is changed. The taskctrldata
       * packet contains a  TTaskCtrlStatusEvent-structure, which has :
       * - the ID of the task
       * - the state of the task
       * - The Estimated time of completion 
       * - The percentage of completition
       *
       * @param[out] aData            Data will be stored here
       * @param[in][out] aSeq         Block until greater sequence number
       *                              available.
       *                              Initially, set the pointed data to 0.
       *                              After that, don't touch the value,
       *                              but pass it on on each call. 
       * @param[in] aTimeout_ms       Timeout for waiting an event
       * @return                      True if new event is arrived, else false
       */
      bool WaitForTaskCtrlEvent(MaCI::TaskCtrl::CTaskCtrlData &aData,
                                unsigned int *INOUT, 
                                unsigned int aTimeout_ms);
      /** Send TaskCtrl Command.
       *
       * Send the given TaskCtrlData element to server side of the TaskCtrl
       * interface.
       *
       * \note This is a basic example for sending a command. Usually
       * interface 'sends()' should be a little more
       * specialized. Like; SetSpeed(), which encapsulates the given
       * parameters to correct container and sends it.  So, condier
       * this function as a \b example call only. Normally, this style
       * of call should be always left as protected/private, and be
       * called by specialized interface public functions only.
       *
       * @param[in] aCmd        Reference to element to send.
       * @param[in] aMessageID  MessageID to set for sent command.
       * @return                'true' if Message was succesfully sent to GIMnet. (No guarantee
       *                        of delivery is given though). 'false' on local connection
       *                        error or invalid datapacket (the container didn't contain
       *                        a TCommand element, which renders this command meaningless)
       */
      bool SendCommand(const MaCI::TaskCtrl::CTaskCtrlData &aCmd, 
                       const unsigned int aMessageID = 0);
       /** Get
       *
       * \note This function acts as an \b example for using the
       * synchronous interface. Its by no means standard function
       * call, and should not be preserved on generated interfaces.
       *       
       * @param[out] aNumber    Reference to unsigned integer to contain the result.
       * @param[in] aTimeout_ms Timeout in milliseconds to wait for the synchronous
       *                        operation to complete.
       * @return                'true' on operation succesfully completed.
       *                         \p sum variable now contains the value 'a+b'.
       */
      
      bool GetDataBySynchronousCall(const MaCI::TaskCtrl::CTaskCtrlData dataIn, 
                                    MaCI::TaskCtrl::CTaskCtrlData &dataOut, 
                                    const unsigned int sequence,
                                    const unsigned int aTimeout_ms);
                                                    
      /** Send TaskCtrl Command with output of a sequence number for receiving.
       * Uses SendCommand.
       *
       * Send the given TaskCtrlData element to server side of the TaskCtrl
       * interface.
       *
       * @param[in] dataIn      Reference to element to send.
       * @param[out] sequence   sequence to set for sent command.
       * @return                'true' if Message was succesfully sent to GIMnet. (No guarantee
       *                        of delivery is given though). 'false' on local connection
       *                        error or invalid datapacket (the container didn't contain
       *                        a TCommand element, which renders this command meaningless)
       */
      bool SimpleDataSend(const MaCI::TaskCtrl::CTaskCtrlData dataIn, 
                          unsigned int &sequence);
       
      /** Receive reply for a specific command.
       *
       * This function attempts to reveive a reply from replyQueue.
       * This wrapper for the MaCI call is only added for an example.
       *
       * \note This is a basic example for sending a command. Usually
       * interface 'sends()' should be a little more
       * specialized. Like; SetSpeed(), which encapsulates the given
       * parameters to correct container and sends it.  So, condier
       * this function as a \b example call only. Normally, this style
       * of call should be always left as protected/private, and be
       * called by specialized interface public functions only.
       *
       * @param[out] aTaskCtrlData Reference to element to send.
       * @param[in] aMessageID  MessageID to set for sent command.
       * @param[in] aTimeout_ms Timeout in milliseconds to wait for message
       *                        to arrive.
       * @return                'true' if reply with given id was succesfully received.
       *                        'false' on any error (including Timeout)
       */
      bool GetCommandReply(MaCI::TaskCtrl::CTaskCtrlData &aTaskCtrlData, 
                           const int aMessageID, 
                           const int aTimeout_ms);
        

      /** Assigns the TaskCtrl client callback function. 
       *
       * This sets a function object for calling a position client
       * callback. If this is set, the interface calls the assigned
       * functions immediately when event is available.
       * 
       * \note As the function is run in the context of the receiving
       * thread, user must make sure that the callback function is
       * fast. As if the function executes too long, receiving events
       * is interrupted.
       *
       * @param[in] aTaskCtrlClientCallback Pointer to class implementing the interface
       *                                 CTaskCtrlClientCallback.
       */
      void SetTaskCtrlClientCallback(MaCI::TaskCtrl::CTaskCtrlClientCallback *aTaskCtrlClientCallback, void * aUserPtr = NULL) {
        iTaskCtrlClientCallback = aTaskCtrlClientCallback;
        iCallBackUserPtr = aUserPtr;
      }
      
      void *GetTaskCtrlClientCallBackUserPtr(void);
      
      CTaskCtrlClientCallback *GetTaskCtrlClientCallBack(void);
      
    protected:
      /** Open client interface.
       *
       * Implementation of Open() for the class. This function is
       * called by the base class CMaCI::Open() function.
       */
      EMaCIError DoOpen(void);
      
      
      /** Close client interface.
       *
       * Implementation of Close() for the class. This function is
       * called by the base class CMaCI::Close() function.
       */
      EMaCIError DoClose(void);


    private:
      /// Thread runner function. Is in charge of receiving events.
      int ThreadFunction(const int aThreadNumber);
      int ProcessMessage(const gimi::GIMIMessage &msg);

      //data.
      void *iCallBackUserPtr;
      unsigned int iTaskCtrlDataSequence; ///< Received data sequence
      unsigned int iTaskCtrlCommandSequence; ///< Last sent Command sequence.
      CTaskCtrlData iLastTaskCtrlData; ///< Last element available
      CTaskCtrlClientCallback *iTaskCtrlClientCallback; ///< Pointer to class instance containing callback functions.
     
      // Override copy & assignment operator privately to disable class copying.
      CTaskCtrlClient(const CTaskCtrlClient &)
        : CSync(1,1),
          CThread(1),
          CMaCI(NULL, 0, 0, ""),
          iCallBackUserPtr(),
          iTaskCtrlDataSequence(),
          iTaskCtrlCommandSequence(),
          iLastTaskCtrlData(),
          iTaskCtrlClientCallback()
        {}
      CTaskCtrlClient &operator=(const CTaskCtrlClient &) { return *this; }

    };
    inline bool CTaskCtrlClient::GetTaskLibrary(MaCI::TaskCtrl::TTaskCtrlTaskStateContainer &aState,
                                                MaCI::TaskCtrl::TStringContainer &aXML,
                                                unsigned int aTimeout_ms)
    {
      TTaskCtrlTaskStateArray stateArray;
      std::vector<std::string> tasksXML;
      bool result = GetTaskLibrary(stateArray,tasksXML,aTimeout_ms);
      if(result) {
          aState = stateArray; 
          aXML = tasksXML;
      } 
      return result;
      
    }
    
      inline bool CTaskCtrlClient::GetTaskTable(MaCI::TaskCtrl::TTaskCtrlTaskStateContainer &aState,
                                               MaCI::TaskCtrl::TStringContainer &aXML,
                                               unsigned int aTimeout_ms)
      {
        TTaskCtrlTaskStateArray stateArray;
        std::vector<std::string> tasksXML;
        bool result = GetTaskTable(stateArray,tasksXML,aTimeout_ms);
        if(result) {
          aState = stateArray; 
          aXML = tasksXML;
        } 
        return result;

      }
      


   


  }
}

#endif //_MACI_INTERFACE_TASKCTRLCLIENT_HPP_
